home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / batchut / rbsetnv1.zip / POPEN.C < prev    next >
Text File  |  1990-10-18  |  14KB  |  437 lines

  1. /*
  2.  * popen/pclose: simple MS-DOS piping scheme to imitate UNIX pipes
  3.  *
  4.  *  [Obtained from SIMTEL20 as popen.arc - no author name on it
  5.  *   Contains a lot of debugging code, which I left in.]
  6.  *   #define DEBUG to get chatty output.
  7.  *
  8.  * Revision record:
  9.  *   08/10/90 RB
  10.  * - Added checks on length of generated command line before attempting to run
  11.  * - Set O_DENYNONE mode on stdin/stdout before running command
  12.  * - Modified naming the pipe file to include PID to guard against nested execution
  13.  *
  14.  *     03/12/90 RB
  15.  * - Changed strsave/strfree to standard strdup/free
  16.  * - Added #ifdef SWITCH to force switchar to / before running shell
  17.  * - Added set_popen_shell() and set_popen_exec() to select method
  18.  *
  19.  *     10/5/89 RB
  20.  * - Changed check for MKS ksh into explicit check for ksh, as opposed to
  21.  *   anything other than command.com (I use 4dos)
  22.  * - Check for trailing "/" or "\" on $TMP - do not add another
  23.  * - Added a simpler version of run() that does not need COMSPEC or getswitch()
  24.  *   and executes the program directly via spawnvp().  It will work for all but
  25.  *   .BAT files or internal commands
  26.  * - Added my own versions of dup() and dup2()
  27.  * - Changed function declarations to prototypes
  28.  * - Added close ostdout/ostdin (original left them open, using up handles)
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include <ctype.h>
  33. #include <alloc.h>
  34. #include <string.h>
  35. #include <errno.h>
  36. #include <setjmp.h>
  37. #include <process.h>
  38. #include <io.h>
  39. #include <dos.h>
  40. #include <fcntl.h>
  41.  
  42. #include "popen.h"
  43.  
  44. extern char *getenv( char * );
  45. extern int getswitch(void);
  46. extern int setswitch(char);
  47.  
  48. #ifndef    _NFILE
  49. #define    _NFILE        OPEN_MAX        /* Number of open files */
  50. #endif    _NFILE
  51.  
  52. #define READIT        1                /* Read pipe */
  53. #define WRITEIT        2                /* Write pipe */
  54. #define MAXARGLINE    127                /* max length of an msdos command line */
  55. #define MAX_ARGS    64                /* max number of separate arguments */
  56. #define TRUE        1
  57. #define FALSE        0
  58.  
  59. static char *prgname[ _NFILE ];        /* program name if write pipe */
  60. static int pipetype[ _NFILE ];        /* 1=read 2=write */
  61. static char *pipename[ _NFILE ];    /* pipe file name */
  62. static int    use_shell = TRUE;        /* flag for type of exec */
  63.  
  64. /*
  65.  *------------------------------------------------------------------------
  66.  * run: Execute command via SHELL or COMSPEC.  This version will run .EXE
  67.  * .COM .BAT or internal commands of the shell program, also pipelines.
  68.  *------------------------------------------------------------------------
  69.  */
  70. static int
  71. runshell( char *command )
  72. {
  73.     jmp_buf panic;                        /* How to recover from errors */
  74.     int lineno;                            /* Line number where panic happened */
  75.     char *shell;                        /* Command processor */
  76.     char *s = (char *) NULL;            /* Holds the command */
  77.     int s_is_malloced = 0;                /* True if need to free 's' */
  78.     static char *command_com = "COMMAND.COM";
  79.     int status;                            /* Return codes */
  80.     char *shellpath;                    /* Full command processor path */
  81.     char *bp;                            /* Generic string pointer */
  82.     char saveswitch;                    /* Save the switch char */
  83.     static char dash_c[ 3 ] = { '?', 'c', '\0' };
  84.     if( (lineno = setjmp( panic )) != 0 ) {
  85.         int E = errno;
  86. #ifdef    DEBUG
  87.         fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E );
  88. #endif    DEBUG
  89.         if( s_is_malloced && (s != (char *) NULL) ) free( s );
  90.         errno = E;
  91.         return( -1 );
  92.     }
  93.     if( (s = strdup( command )) == (char *) NULL ) longjmp( panic, __LINE__ );
  94.  
  95.     /* Determine the command processor - use SHELL and then COMSPEC */
  96.     if( ((shell = getenv( "SHELL" ))   == (char *) NULL) &&
  97.         ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com;
  98.     strupr( shell );
  99.     shellpath = shell;
  100.  
  101.     /* Strip off any leading backslash directories */
  102.     shell = strrchr( shellpath, '\\' );
  103.     if( shell != (char *) NULL ) ++shell;
  104.     else                         shell = shellpath;
  105.  
  106.     /* Strip off any leading slash directories */
  107.     bp = strrchr( shell, '/'  );
  108.     if( bp != (char *) NULL ) shell = ++bp;
  109.     if( strstr( shell, "KSH" ) != NULL ) {
  110.         /* MKS Shell needs quoted argument */
  111.         char *bp;
  112.         if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL )
  113.              longjmp( panic, __LINE__ );
  114.         *bp++ = '\'';
  115.         while( (*bp++ = *command++) != '\0' );
  116.         *(bp - 1) = '\'';
  117.         *bp = '\0';
  118.         s_is_malloced = 1;
  119.     } else s = command;
  120.     saveswitch = dash_c[ 0 ] = (char) getswitch();
  121. #ifdef SWITCH  /* some shells will not work without '/' so force it here */
  122.     setswitch('/');
  123.     dash_c[ 0 ] = '/';
  124. #endif
  125.     /* Test the length of the generated command line - if too long, fail */
  126.     if (strlen(s) + strlen(shell) + 4 > MAXARGLINE) {
  127. #ifdef DEBUG
  128.         fprintf(stderr,"popen: command line too long\n");
  129. #endif DEBUG
  130.         errno = E2BIG;
  131.         return(-1);
  132.     }
  133.     /* Run the program */
  134. #ifdef    DEBUG
  135.     fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s );
  136. #endif    DEBUG
  137.     status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL );
  138.     if( s_is_malloced ) free( s );
  139. #ifdef SWITCH                          /* restore switchar if we changed it */
  140.     setswitch(saveswitch);
  141. #endif
  142.     return( status );
  143. }
  144.  
  145.  
  146. /*
  147.  *------------------------------------------------------------------------
  148.  * run: Execute command directly with spawnvp.  This version will only run
  149.  * single .EXE or .COM files but is much faster than using COMSPEC
  150.  *------------------------------------------------------------------------
  151.  */
  152. /*
  153.  * If we know something about the program to be run, we can execute it
  154.  * directly instead of calling the shell, thereby saving some time and memory
  155.  * This routine added by R. Brittain
  156.  */
  157. static int
  158. runexec( char *command )
  159. {
  160.     char *vals[MAX_ARGS];                /* array of pointers to the arguments */
  161.     char cmd[MAXARGLINE+1], *s;
  162.     int  cnt=0, totlen=0, status, i;
  163.  
  164.     /* Copy command and parse argument list. No support for \" in this version */
  165.     s = cmd;
  166.     strcpy(s,command);                    /* copy the argument - we will modify this */
  167.  
  168.     while (TRUE) {
  169.         s += strspn(s, " \t\n");        /* skip leading whitespace */
  170.         if (*s == '\0')
  171.              break;
  172.  
  173.         if (cnt >= MAX_ARGS) {
  174. #ifdef DEBUG
  175.             fprintf(stderr,"popen: too many arguments in call to exec\n");
  176. #endif DEBUG
  177.             return(-1);
  178.         }
  179.         if (*s == '"') {                /* open quote - look for closing quote */
  180.             vals[cnt] = ++s;            /* quotes are not included in argument */
  181.             if ((s = strchr(s, '"')) == NULL)
  182.                 break;
  183.             ++cnt;
  184.             *s++ = '\0';                /* terminate this argument */
  185.         } else {
  186.             vals[cnt++] = s;            /* start of a new argument */
  187.             s += strcspn(s, " \n\t");
  188.             if (*s == '\0')
  189.                 break;
  190.             else
  191.                 *s++ = '\0';
  192.         }
  193.     }
  194.     vals[cnt] = NULL;                    /* terminate array with a null pointer */
  195.     /*
  196.      * Test the final parsed arguments against maximum allowable command line
  197.      * before executing.  If too long, fail.
  198.      */
  199.     for (i=0; vals[i] != NULL; i++) {
  200.         totlen += strlen(vals[i]) + 1;
  201.         if (totlen > MAXARGLINE) {
  202. #ifdef DEBUG
  203.             fprintf(stderr,"popen: command line too long\n");
  204. #endif DEBUG
  205.             errno = E2BIG;
  206.             return(-1);
  207.         }
  208.     }
  209. #ifdef    DEBUG
  210.     fprintf( stderr, "Running: %s\n", command );
  211. #endif    DEBUG
  212.     status = spawnvp( P_WAIT, vals[0], vals);
  213.     return( status );
  214. }
  215.  
  216.  
  217. /*
  218.  *------------------------------------------------------------------------
  219.  * uniquepipe: returns a unique file name to use as a pipe
  220.  *------------------------------------------------------------------------
  221.  */
  222. static char *
  223. uniquepipe(void)
  224. {
  225.     static char name[ 14 ];
  226.     static short int num = 0;
  227.     (void) sprintf( name, "p%04x%03.3d.tmp", getpid(), num++ );
  228.     return( name );
  229. }
  230.  
  231. /*
  232.  *------------------------------------------------------------------------
  233.  * resetpipe: Private routine to cancel a pipe
  234.  *------------------------------------------------------------------------
  235.  */
  236. static void
  237. resetpipe( int fd )
  238. {
  239.     char *bp;
  240.     if( (fd >= 0) && (fd < _NFILE) ) {
  241.         pipetype[ fd ] = 0;
  242.         if( (bp = pipename[ fd ]) != (char *) NULL ) {
  243.             (void) unlink( bp );
  244.             free( bp );
  245.             pipename[ fd ] = (char *) NULL;
  246.         }
  247.         if( (bp = prgname[ fd ]) != (char *) NULL ) {
  248.             free( bp );
  249.             prgname[ fd ] = (char *) NULL;
  250.         }
  251.     }
  252. }
  253.  
  254. /*
  255.  *-------------------------------------------------------------